package com.tiejun.ge.mvc.aspect;

import cn.hutool.core.util.ObjectUtil;
import com.google.common.util.concurrent.RateLimiter;
import com.tiejun.ge.mvc.annotation.RateLimit;
import com.tiejun.ge.mvc.config.SystemCatch;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @program: springboot
 * @description: RateLimit切面
 * @author: getiejun
 * @create: 2025-08-23 23:13
 **/
@Slf4j
@Aspect
@Component
public class RateLimitAspect {

    @Resource
    private SystemCatch systemCatch;

    private static final ConcurrentHashMap<String, Object> rateLimitMap = new ConcurrentHashMap<>();

    private static final String RATE_LIMIT_KEY = "system_rate_limit";

    @Pointcut("@annotation(com.tiejun.ge.mvc.annotation.RateLimit)")
    public void rateLimit() {
    }

    @Around("rateLimit()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        RateLimit annotation = AnnotationUtils.findAnnotation(method, RateLimit.class);
        //Double.parseDouble((String) systemCatch.getOrDefault("system_rate_limit", annotation.limit())))
        RateLimiter rateLimiter = (RateLimiter) rateLimitMap.computeIfAbsent(RATE_LIMIT_KEY, k -> RateLimiter.create(ObjectUtil.isEmpty(annotation.limit()) ? Double.parseDouble((String) systemCatch.getOrDefault("system_rate_limit", "5")) : annotation.limit()));

        // 尝试获取令牌
        if (rateLimiter != null && rateLimiter.tryAcquire()) {
            return joinPoint.proceed();
        } else {
            throw new RuntimeException("too many requests, please try again later...");
        }
    }
}
